import sharp from 'sharp';
import { promises as fs } from 'fs';
import path from 'path';
import { SharpOptions, ProcessedImage, SharpProcessor } from './types.js';

export class ImageProcessor {
  private options: SharpOptions;
  private stats = {
    totalFiles: 0,
    processedFiles: 0,
    totalOriginalSize: 0,
    totalCompressedSize: 0,
    processingTime: 0
  };

  constructor(options: SharpOptions = {}) {
    this.options = this.normalizeOptions(options);
  }

  private normalizeOptions(options: SharpOptions): SharpOptions {
    return {
      quality: {
        jpeg: 82,
        png: 85,
        webp: 78,
        avif: 70,
        ...options.quality
      },
      compress: {
        jpeg: {
          quality: options.quality?.jpeg || 82,
          progressive: true,
          mozjpeg: true,
          optimiseScans: true,
          ...options.compress?.jpeg
        },
        png: {
          quality: options.quality?.png || 85,
          compressionLevel: 9,
          palette: true, // 启用调色板优化
          adaptiveFiltering: true,
          ...options.compress?.png
        },
        webp: {
          quality: options.quality?.webp || 78,
          effort: 6,
          lossless: false,
          nearLossless: false,
          smartSubsample: true,
          ...options.compress?.webp
        },
        avif: {
          quality: options.quality?.avif || 70,
          effort: 6, // 提高压缩效率
          chromaSubsampling: '4:2:0',
          ...options.compress?.avif
        },
        ...options.compress
      },
      // 保持 convert 的原始形态（字符串或旧对象），避免类型冲突
      convert: options.convert,
      filter: {
        minSize: 512, // 降低到 512 bytes，处理更多小图片
        include: ['.jpg', '.jpeg', '.png', '.webp', '.avif', '.tiff'],
        exclude: [],
        ...options.filter
      },
      performance: {
        concurrency: 4,
        cache: true,
        ...options.performance
      },
      debug: options.debug || false,
      ...options
    };
  }

  /**
   * 检查文件是否应该被处理
   */
  private shouldProcessFile(filePath: string, fileSize: number): boolean {
    const { filter } = this.options;
    
    // 检查文件大小
    if (filter?.minSize && fileSize < filter.minSize) return false;
    if (filter?.maxSize && fileSize > filter.maxSize) return false;

    // 检查文件扩展名
    const ext = path.extname(filePath).toLowerCase();
    if (filter?.include && !filter.include.includes(ext)) return false;
    if (filter?.exclude && filter.exclude.includes(ext)) return false;

    // 检查路径模式
    if (filter?.includePaths) {
      const matchesInclude = filter.includePaths.some(pattern => {
        if (typeof pattern === 'string') {
          return filePath.includes(pattern);
        }
        return pattern.test(filePath);
      });
      if (!matchesInclude) return false;
    }

    if (filter?.excludePaths) {
      const matchesExclude = filter.excludePaths.some(pattern => {
        if (typeof pattern === 'string') {
          return filePath.includes(pattern);
        }
        return pattern.test(filePath);
      });
      if (matchesExclude) return false;
    }

    return true;
  }

  /**
   * 获取输出文件路径
   */
  private getOutputPath(originalPath: string, format?: string): string {
    const { output } = this.options;
    const parsedPath = path.parse(originalPath);
    
    let filename = parsedPath.name;
    let extension = format ? `.${format}` : parsedPath.ext;
    
    if (output?.filename) {
      filename = output.filename
        .replace('[name]', parsedPath.name)
        .replace('[hash]', this.generateHash(originalPath))
        .replace('[ext]', extension.slice(1));
      extension = '';
    }

    const outputDir = output?.dir || parsedPath.dir;
    return path.join(outputDir, filename + extension);
  }

  /**
   * 生成文件哈希
   */
  private generateHash(filePath: string): string {
    // 简单的哈希生成，实际项目中可能需要更复杂的实现
    return Math.random().toString(36).substring(2, 8);
  }

  /**
   * 处理单个图像
   */
  async processImage(inputPath: string, outputPath?: string): Promise<ProcessedImage[]> {
    const startTime = Date.now();
    const results: ProcessedImage[] = [];
    
    try {
      // 读取原始文件
      const inputBuffer = await fs.readFile(inputPath);
      const originalSize = inputBuffer.length;
      
      // 检查是否应该处理这个文件
      if (!this.shouldProcessFile(inputPath, originalSize)) {
        if (this.options.debug) {
          console.log(`Skipping ${inputPath} (filtered out)`);
        }
        return results;
      }

      this.stats.totalFiles++;
      this.stats.totalOriginalSize += originalSize;

      // 创建 Sharp 实例
      const sharpInstance = sharp(inputBuffer);
      const metadata = await sharpInstance.metadata();

      // 解析转换配置
      const convertTarget = this.getConvertTarget(); // 'webp' | 'avif' | undefined
      const allowedFrom = this.getAllowedFrom();
      const convertMode = this.getConvertMode(); // 'replace' | 'sidecar'
      const srcFormat = (metadata.format || '').toLowerCase();
      const isConvertible = srcFormat && allowedFrom.includes(srcFormat);
      const needConvert = Boolean(convertTarget && isConvertible);

      let mainProducedSize = 0;

      // replace 且需要转换：直接从原始管线转换为目标格式
      if (needConvert && convertMode === 'replace') {
        const convBuffer = await this.convertBuffer(sharpInstance.clone(), convertTarget as 'webp' | 'avif');
        const convPath = this.getOutputPath(inputPath, convertTarget as string);
        await this.ensureDirectoryExists(path.dirname(convPath));
        await fs.writeFile(convPath, convBuffer);

        results.push({
          originalPath: inputPath,
          outputPath: convPath,
          originalSize,
          compressedSize: convBuffer.length,
          format: convertTarget as string,
          width: metadata.width,
          height: metadata.height
        });

        this.stats.totalCompressedSize += convBuffer.length;
        mainProducedSize = convBuffer.length;
      } else {
        // 非 replace 或无需转换：先按原格式压缩，再（如需）sidecar 转换
        let compressedBuffer: Buffer;
        try {
          compressedBuffer = await this.compressImage(sharpInstance.clone(), metadata.format);
        } catch (e) {
          if (this.options.debug) {
            console.error(`Compress failed, fallback to original for ${inputPath}:`, e);
          }
          compressedBuffer = await sharpInstance.toBuffer();
        }

        const mainOutputPath = outputPath || this.getOutputPath(inputPath);
        await this.ensureDirectoryExists(path.dirname(mainOutputPath));
        await fs.writeFile(mainOutputPath, compressedBuffer);

        results.push({
          originalPath: inputPath,
          outputPath: mainOutputPath,
          originalSize,
          compressedSize: compressedBuffer.length,
          format: metadata.format || 'unknown',
          width: metadata.width,
          height: metadata.height
        });

        this.stats.totalCompressedSize += compressedBuffer.length;
        mainProducedSize = compressedBuffer.length;

        if (needConvert && convertMode === 'sidecar') {
          const sideBuffer = await this.convertBuffer(sharpInstance.clone(), convertTarget as 'webp' | 'avif');
          const sidePath = this.getOutputPath(inputPath, convertTarget as string);
          await fs.writeFile(sidePath, sideBuffer);

          results.push({
            originalPath: inputPath,
            outputPath: sidePath,
            originalSize,
            compressedSize: sideBuffer.length,
            format: convertTarget as string,
            width: metadata.width,
            height: metadata.height
          });
        }
      }

      // 响应式图片处理
      if (this.options.responsive?.sizes) {
        const responsiveResults = await this.generateResponsiveImages(
          sharpInstance.clone(),
          inputPath,
          metadata
        );
        results.push(...responsiveResults);
      }

      this.stats.processedFiles++;
      
      if (this.options.debug) {
        const processingTime = Date.now() - startTime;
        console.log(`Processed ${inputPath} in ${processingTime}ms`);
        console.log(`Original: ${this.formatBytes(originalSize)}, Output: ${this.formatBytes(mainProducedSize)}`);
      }

    } catch (error) {
      console.error(`Error processing ${inputPath}:`, error);
      throw error;
    }

    return results;
  }

  /** 解析转换目标（兼容字符串与旧对象） */
  private getConvertTarget(): 'webp' | 'avif' | undefined {
    const c = this.options.convert as any;
    if (!c) return undefined;
    if (typeof c === 'string') return c === 'webp' || c === 'avif' ? c : undefined;
    if (typeof c === 'object') {
      if (c.toWebP) return 'webp';
      if (c.toAVIF) return 'avif';
    }
    return undefined;
  }

  /** 获取允许的来源格式（默认 jpeg/jpg/png） */
  private getAllowedFrom(): string[] {
    const def = ['jpeg', 'jpg', 'png'];
    if (Array.isArray(this.options.convertFrom) && this.options.convertFrom.length) return this.options.convertFrom as unknown as string[];
    return def;
  }

  /** 获取转换模式（默认由 deleteOriginal 推断） */
  private getConvertMode(): 'sidecar' | 'replace' {
    if (this.options.convertMode === 'sidecar' || this.options.convertMode === 'replace') return this.options.convertMode;
    return this.options.deleteOriginal ? 'replace' : 'sidecar';
  }

  /** 将缓冲区转换为指定格式 */
  private async convertBuffer(sharpInstance: sharp.Sharp, target: 'webp' | 'avif'): Promise<Buffer> {
    if (target === 'webp') return this.convertToWebP(sharpInstance);
    return this.convertToAVIF(sharpInstance);
  }

  /**
   * 压缩图像
   */
  private async compressImage(sharpInstance: sharp.Sharp, format?: string): Promise<Buffer> {
    const { compress } = this.options;

    switch (format) {
      case 'jpeg':
        return sharpInstance
          .jpeg({
            quality: compress?.jpeg?.quality,
            progressive: compress?.jpeg?.progressive,
            mozjpeg: compress?.jpeg?.mozjpeg
          })
          .toBuffer();

      case 'png':
        // 仅传入已定义的选项，避免 sharp 接收到非法参数
        const pngOpts: sharp.PngOptions = {};
        if (typeof compress?.png?.compressionLevel === 'number') {
          pngOpts.compressionLevel = compress.png.compressionLevel;
        }
        if (typeof compress?.png?.palette === 'boolean') {
          pngOpts.palette = compress.png.palette;
        }
        if (typeof compress?.png?.adaptiveFiltering === 'boolean') {
          pngOpts.adaptiveFiltering = compress.png.adaptiveFiltering;
        }
        // quality 仅在 palette 量化启用时有效
        if (pngOpts.palette === true && typeof compress?.png?.quality === 'number') {
          pngOpts.quality = compress.png.quality;
        }
        return sharpInstance.png(pngOpts).toBuffer();

      case 'webp':
        return sharpInstance
          .webp({
            quality: compress?.webp?.quality,
            effort: compress?.webp?.effort,
            lossless: compress?.webp?.lossless
          })
          .toBuffer();

      case 'avif':
        return sharpInstance
          .avif({
            quality: compress?.avif?.quality,
            effort: compress?.avif?.effort
          })
          .toBuffer();

      default:
        // 默认保持原格式
        return sharpInstance.toBuffer();
    }
  }

  /**
   * 转换为 WebP
   */
  private async convertToWebP(sharpInstance: sharp.Sharp): Promise<Buffer> {
    const { compress } = this.options;
    return sharpInstance
      .webp({
        quality: compress?.webp?.quality,
        effort: compress?.webp?.effort,
        lossless: compress?.webp?.lossless
      })
      .toBuffer();
  }

  /**
   * 转换为 AVIF
   */
  private async convertToAVIF(sharpInstance: sharp.Sharp): Promise<Buffer> {
    const { compress } = this.options;
    return sharpInstance
      .avif({
        quality: compress?.avif?.quality,
        effort: compress?.avif?.effort
      })
      .toBuffer();
  }

  /**
   * 生成响应式图片
   */
  private async generateResponsiveImages(
    sharpInstance: sharp.Sharp,
    originalPath: string,
    metadata: sharp.Metadata
  ): Promise<ProcessedImage[]> {
    const results: ProcessedImage[] = [];
    const { responsive } = this.options;
    
    if (!responsive?.sizes || !metadata.width) return results;

    for (const size of responsive.sizes) {
      // 跳过比原图更大的尺寸
      if (size > metadata.width) continue;

      const formats = responsive.formats || ['jpeg'];
      
      for (const format of formats) {
        const resizedInstance = sharpInstance.clone().resize(size);
        const buffer = await this.compressImage(resizedInstance, format);
        
        const outputPath = this.getOutputPath(
          originalPath.replace(path.extname(originalPath), `_${size}w.${format}`)
        );
        
        await this.ensureDirectoryExists(path.dirname(outputPath));
        await fs.writeFile(outputPath, buffer);

        results.push({
          originalPath,
          outputPath,
          originalSize: metadata.size || 0,
          compressedSize: buffer.length,
          format,
          width: size,
          height: Math.round((size / metadata.width) * (metadata.height || 0))
        });
      }
    }

    return results;
  }

  /**
   * 确保目录存在
   */
  private async ensureDirectoryExists(dirPath: string): Promise<void> {
    try {
      await fs.access(dirPath);
    } catch {
      await fs.mkdir(dirPath, { recursive: true });
    }
  }

  /**
   * 格式化字节数
   */
  private formatBytes(bytes: number): string {
    if (bytes === 0) return '0 Bytes';
    const k = 1024;
    const sizes = ['Bytes', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  }

  /**
   * 获取处理统计信息
   */
  getStats() {
    return {
      ...this.stats,
      compressionRatio: this.stats.totalOriginalSize > 0 
        ? (1 - this.stats.totalCompressedSize / this.stats.totalOriginalSize) * 100
        : 0
    };
  }

  /**
   * 重置统计信息
   */
  resetStats() {
    this.stats = {
      totalFiles: 0,
      processedFiles: 0,
      totalOriginalSize: 0,
      totalCompressedSize: 0,
      processingTime: 0
    };
  }
}
